package com.lsh.payment.service.payment;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.dubbo.rpc.protocol.rest.support.ContentType;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lsh.payment.api.model.basevo.BaseResponse;
import com.lsh.payment.api.model.basevo.ExceptionStatus;
import com.lsh.payment.api.model.payment.PaymentRequest;
import com.lsh.payment.api.model.payment.PaymentTogetherRequest;
import com.lsh.payment.api.service.payment.IPayRestService;
import com.lsh.payment.core.constant.RedisKeyConstant;
import com.lsh.payment.core.dao.redis.RedisStringDao;
import com.lsh.payment.core.exception.BusinessException;
import com.lsh.payment.core.model.config.PayConfig;
import com.lsh.payment.core.model.payasync.PayMonitorInterfaceModel;
import com.lsh.payment.core.model.payenum.*;
import com.lsh.payment.core.service.allinpay.impl.AllinPayServiceImpl;
import com.lsh.payment.core.service.async.PayEventBus;
import com.lsh.payment.core.service.config.ConfigService;
import com.lsh.payment.core.service.payment.IPayChannelService;
import com.lsh.payment.core.service.redis.RedisLockService;
import com.lsh.payment.core.util.DateUtil;
import com.lsh.payment.core.util.PayAssert;
import com.lsh.payment.service.BaseService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;

/**
 * Project Name: lsh-payment
 *
 * @author peter
 * @date 16/11/8
 * 北京链商电子商务有限公司
 * Package
 * desc:
 */
@Service(protocol = "rest", validation = "true")
@Path("payment")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({ContentType.APPLICATION_JSON_UTF_8})
public class PaymentService extends BaseService implements IPayRestService {

    private final static String HTTP_PREFIX = "http";

    private final static int INIT_NUM = 10;

    private final static String STRATEGY_FLAG = "yes";

    private final Logger logger = LoggerFactory.getLogger(PaymentService.class);

    private static Map<String, String> allinStrategyMap;

    private static Map<String, String> cmStrategyMap;

    private static List<String> prefixWxList;

    private static List<String> prefixAliList;

    @Autowired
    private Map<String, IPayChannelService> payServices;

    @Autowired
    private AllinPayServiceImpl AllinPayService;

    @Autowired
    private RedisLockService redisLockService;

    @Autowired
    private ConfigService configService;

    @Autowired
    private RedisStringDao redisStringDao;

    @Value("${payment.create.strategy}")
    private String strategy;

    @Value("${payment.create.qf.percentage}")
    private Integer qfPercentage;

    @Value("${payment.create.allin.percentage}")
    private Integer allinPercentage;

    @Value("${payment.create.cm.percentage}")
    private Integer cmPercentage;

    @Value("${payment.create.billPercentage}")
    private Integer billPercentage;

    static {
        allinStrategyMap = new HashMap<>();
        cmStrategyMap = new HashMap<>();

        prefixWxList = new ArrayList<>();
        prefixAliList = new ArrayList<>();

        //扫码策略切换map
        allinStrategyMap.put(TradeType.QFWXSM.getCode() + "", TradeType.ALLINWXSM.getCode() + "");
        allinStrategyMap.put(TradeType.QFALISM.getCode() + "", TradeType.ALLINALISM.getCode() + "");

        cmStrategyMap.put(TradeType.QFWXSM.getCode() + "", TradeType.CMWXSM.getCode() + "");
        cmStrategyMap.put(TradeType.QFALISM.getCode() + "", TradeType.CMALISM.getCode() + "");

        // 微信被扫支付前缀
        prefixWxList = Arrays.asList("10", "11", "12", "13", "14", "15");

        // 支付宝被扫支付前缀
        prefixAliList = Arrays.asList("25", "26", "27", "28", "29", "30");
    }


    @POST
    @Path("prepayment")
    @Override
    public BaseResponse prePayment(PaymentRequest paymentRequest) {
        BaseResponse baseResponse = new BaseResponse();

        logger.info("【支付平台统一下单请求】: {} .", JSON.toJSONString(paymentRequest));
        long beginTime = System.currentTimeMillis();
        String key = MessageFormat.format(RedisKeyConstant.PAY_TRADE_LOCK, paymentRequest.getTrade_id());
        try {

            Long venderId = paymentRequest.getVenderId();
            if(venderId == null){
                paymentRequest.setVenderId(this.getVenderId());
            }
            if(paymentRequest.getVenderId() == null || paymentRequest.getVenderId() == 0L){
                throw new BusinessException(ExceptionStatus.E1002002.getCode(), "venderId 租户信息必传");
            }
            //重复下单锁
            if (!redisLockService.lock(key, paymentRequest.getTrade_id())) {
                throw new BusinessException(ExceptionStatus.E1002002.getCode(), "支付处理中,请稍等...");
            }

            if (!paymentRequest.getNotify_url().startsWith(HTTP_PREFIX)) {
                throw new BusinessException(ExceptionStatus.E2001002.getCode(), "notify_url 参数异常");
            }

            if (StringUtils.isBlank(paymentRequest.getTrade_id())) {
                throw new BusinessException(ExceptionStatus.E2001002.getCode(), "trade_id 必填");
            }

            if (paymentRequest.getTrade_type().equals(TradeType.PURSE_BALANCE_PAY.getCode() + "")) {
                if (BigDecimal.ZERO.equals(new BigDecimal(paymentRequest.getRequest_amount()))) {
                    throw new BusinessException(ExceptionStatus.E1002002.getCode(), "支付金额不能是0");
                }
            } else {
                PayAssert.isAmount(paymentRequest.getRequest_amount(), ExceptionStatus.E1002001.getCode(), "支付金额不合法,小数点后最多两位,且大于0,例如(12.25,0.88)");
            }

            this.strategyFilter(paymentRequest);

            String serviceName = PayService.getServiceByCode(paymentRequest.getTrade_type());
            if (StringUtils.isBlank(serviceName)) {
                throw new BusinessException(ExceptionStatus.E2001002.getCode(), "channelType 参数异常");
            }

            logger.info("[paymentRequest.getSystem is] " + paymentRequest.getSystem());
            if (paymentRequest.getSystem() == null) {
                paymentRequest.setSystem(CallSystem.YG.getCode());
            }

            if (paymentRequest.getSystem_channel() == null) {
                paymentRequest.setSystem_channel(CallSystem.YG.getCode());
            }

            if (paymentRequest.getPay_type() == null) {
                paymentRequest.setPay_type(PayType.PAY.getCode());
            }
            logger.info("[paymentRequest.getSystem is must] " + paymentRequest.getSystem());
            baseResponse = payServices.get(serviceName).prepay(paymentRequest);
        } catch (BusinessException e) {
            baseResponse.setRet(Integer.parseInt(e.getCode()));
            baseResponse.setMsg(e.getMessage());
            logger.error("【业务异常】:code is {},message is {}.", new String[]{e.getCode(), e.getMessage()});
        } catch (Exception e) {
            baseResponse.setRet(Integer.parseInt(ExceptionStatus.E3001001.getCode()));
            baseResponse.setMsg(ExceptionStatus.E3001001.getMessage());
            logger.error("【服务端异常】", e);
        } finally {
            try {
                if (baseResponse.getRet() == Integer.parseInt(ExceptionStatus.SUCCESS.getCode())) {
                    redisLockService.unlock(key);
                }
            } catch (Exception e) {
                logger.error("【redis 操作异常】", e);
            }
        }

        try {
            long wasteTime = System.currentTimeMillis() - beginTime;
            logger.info("【tradeid is {}】 【统一下单接口耗时】 {} 毫秒。", paymentRequest.getTrade_id(), wasteTime);
            String wasteTimeStr = (new StringBuffer(DateUtil.nowStrFormate())).append("[").append(wasteTime).append("]").toString();
            PayMonitorInterfaceModel payMonitorInterfaceModel = new PayMonitorInterfaceModel(paymentRequest, wasteTimeStr, this.getClass().getSimpleName());
            if (!baseResponse.getRet().toString().equals(ExceptionStatus.SUCCESS.getCode())) {
                payMonitorInterfaceModel.setResultFlag(false);
            }
            PayEventBus.post(payMonitorInterfaceModel);
        } catch (Throwable e) {
            logger.error("【收集监控信息失败】", e);
        }

        return baseResponse;
    }

    /**
     * 支付平台统一下单接口
     *
     * @param togetherRequest 下单对象
     * @return BaseResponse 返回值对象
     */
    @POST
    @Path("together")
    @Override
    public BaseResponse togetherPayment(PaymentTogetherRequest togetherRequest) {
        BaseResponse baseResponse = new BaseResponse();

        logger.info("【聚合支付统一下单请求】: {} .", JSON.toJSONString(togetherRequest));

        try {

            PaymentRequest paymentRequest = new PaymentRequest();
            BeanUtils.copyProperties(togetherRequest, paymentRequest);
            String route_id = togetherRequest.getWill_code();
            String pOrderCode = togetherRequest.getP_order_code();
            String tradeId = route_id + "_" + pOrderCode;

            List<String> trades = togetherRequest.getTogether_trades();
            JSONObject json = new JSONObject();

            json.put("route_id", route_id);
            json.put("will_code", route_id);
            json.put("p_order_code", pOrderCode);
            if (CollectionUtils.isEmpty(trades)) {
                trades = Collections.EMPTY_LIST;
            }
            json.put("together_trades", trades);

            paymentRequest.setTrade_id(tradeId);
            paymentRequest.setExt(json.toJSONString());
            paymentRequest.setPay_type(PayType.TOGETHER_PAY.getCode());

            paymentRequest.setVenderId(this.getVenderId());

            baseResponse = this.prePayment(paymentRequest);

        } catch (BusinessException e) {

            baseResponse.setRet(Integer.parseInt(e.getCode()));
            baseResponse.setMsg(e.getMessage());
            logger.error("【业务异常】:code is {},message is {}.", new String[]{e.getCode(), e.getMessage()});
        } catch (Exception e) {

            baseResponse.setRet(Integer.parseInt(ExceptionStatus.E3001001.getCode()));
            baseResponse.setMsg(ExceptionStatus.E3001001.getMessage());
            logger.error("【服务端异常】", e);
        }

        return baseResponse;
    }

    /**
     * 支付平台支付单接口
     *
     * @param paymentRequest 下单对象
     * @return BaseResponse 返回值对象
     */
    @Override
    @POST
    @Path("codePay")
    public BaseResponse codePay(PaymentRequest paymentRequest) {

        BaseResponse baseResponse = new BaseResponse();

        logger.info("【支付平台支付反扫请求】: {} .", JSON.toJSONString(paymentRequest));

        String key = MessageFormat.format(RedisKeyConstant.PAY_CODE_TRADE_LOCK, paymentRequest.getTrade_id());
        try {
            //重复下单锁
            if (!redisLockService.lock(key, paymentRequest.getTrade_id())) {
                throw new BusinessException(ExceptionStatus.E1002002.getCode(), "支付处理中,请稍等...");
            }

            if (!paymentRequest.getNotify_url().startsWith(HTTP_PREFIX)) {
                throw new BusinessException(ExceptionStatus.E2001002.getCode(), "notify_url 参数异常");
            }

            PayAssert.isAmount(paymentRequest.getRequest_amount(), ExceptionStatus.E1002001.getCode(), "支付金额不合法,小数点后最多两位,且大于0,例如(12.25,0.88)");

            PayAssert.notNull(paymentRequest.getAuthcode(), ExceptionStatus.E1002001.getCode(), "支付授权码不能为空");
//            logger.info("【paymentRequest.getSystem is】 " + paymentRequest.getSystem());
            if (paymentRequest.getSystem() == null) {
                paymentRequest.setSystem(2);
            }
            logger.info("【paymentRequest.getSystem is must】 " + paymentRequest.getSystem());

            this.strategyTradeType(paymentRequest);

            baseResponse = AllinPayService.codePay(paymentRequest);
        } catch (BusinessException e) {
            baseResponse.setRet(Integer.parseInt(e.getCode()));
            baseResponse.setMsg(e.getMessage());
            logger.error("【业务异常:code is {}】,【message is】{}.", new String[]{e.getCode(), e.getMessage()});
        } catch (Exception e) {
            baseResponse.setRet(Integer.parseInt(ExceptionStatus.E3001001.getCode()));
            baseResponse.setMsg(ExceptionStatus.E3001001.getMessage());
            logger.error("服务端异常", e);
        } finally {
            try {
                redisLockService.unlock(key);
            } catch (Exception e) {
                logger.error("【redis 操作异常】", e);
            }
        }

        return baseResponse;
    }

    private void afterProperties() {
        PayConfig config = configService.getConfigOne(1);

        logger.info("【下单策略配置信息】[{}]", JSON.toJSONString(config));

        if (config != null) {
            String content = config.getContent();

            JSONObject payConfig = JSONObject.parseObject(content);

            strategy = payConfig.getString("strategy");

//            percentage = payConfig.getInteger("percentage");

            billPercentage = payConfig.getInteger("billPercentage");
        }
    }

    /**
     * @param paymentRequest
     */
    private void strategyTradeType(PaymentRequest paymentRequest) {
        String filterPrefix = "【被扫策略】[" + paymentRequest.getTrade_id() + "] ";
        String authCode = paymentRequest.getAuthcode();
        String prefix = authCode.substring(0, 2);

        if (prefixWxList.contains(prefix)) {
            paymentRequest.setTrade_type(TradeType.ALLINWXBSM.getCode() + "");
        } else if (prefixAliList.contains(prefix)) {
            paymentRequest.setTrade_type(TradeType.ALLINALIBSM.getCode() + "");
        } else {
            throw new BusinessException(ExceptionStatus.E2001002.getCode(), "支付授权码格式错误");
        }

        paymentRequest.setExt(paymentRequest.getAuthcode());

        logger.info("【{}】 【处理后参数】 {}", filterPrefix, JSON.toJSONString(paymentRequest));
    }

    /**
     * @param paymentRequest
     */
    private void strategyFilter(PaymentRequest paymentRequest) {
        String filterPrefix = "【下单扫码策略】[" + paymentRequest.getTrade_id() + "] ";

        if (StringUtils.isNotBlank(strategy) && !strategy.equals(STRATEGY_FLAG)) {
            logger.info("【{}】【strategy is {}】", filterPrefix, strategy);
            return;
        }
        int tradeType = Integer.parseInt(paymentRequest.getTrade_type());
        if (tradeType != TradeType.QFWXSM.getCode() && tradeType != TradeType.QFALISM.getCode()) {
            logger.info("【{}】【channelType is {}】", filterPrefix, paymentRequest.getTrade_type());
            return;
        }

        // TODO Channel_type 处理 待优化
        PayAssert.notNull(paymentRequest.getTrade_module(), ExceptionStatus.E1002002.getCode(), ExceptionStatus.E1002002.getMessage());

//        if (paymentRequest.getTrade_module().equals(TradeModule.BILL.getName())) {
//            String billTimesKey = RedisKeyConstant.PAY_BILL_TRADE_TIMES;
//            Long billTradeTimes = redisStringDao.increment(billTimesKey);
//
//            logger.info("【{} redis】【billTradeTimes is {}】", filterPrefix, billTradeTimes);
//            int billLastNum = 0;
//            if (billTradeTimes <= INIT_NUM) {
//                billLastNum = billTradeTimes.intValue();
//            } else {
//                String tradeTimesStr = billTradeTimes.toString();
//                String _lastNum = tradeTimesStr.substring(tradeTimesStr.length() - 2);
//                if (StringUtils.isNotBlank(_lastNum)) {
//                    billLastNum = Integer.parseInt(_lastNum);
//                }
//            }
//
//            if (billLastNum < billPercentage) {
//                String chanType = allinStrategyMap.get(paymentRequest.getTrade_type());
//                if (StringUtils.isNotBlank(chanType)) {
//                    paymentRequest.setTrade_type(chanType);
//                }
//            }
//
//            logger.info("【{} strategy】 【paymentRequest = {}】", filterPrefix, JSON.toJSONString(paymentRequest));
//            return;
//        }

        String timesKey = RedisKeyConstant.PAY_TRADE_TIMES;
        Long tradeTimes = redisStringDao.increment(timesKey);
        logger.info("【{} redis】【tradeTimes = {}】", filterPrefix, tradeTimes);
        int orderLastNum = 0;
        if (tradeTimes != null) {

            if (tradeTimes <= INIT_NUM) {
                orderLastNum = tradeTimes.intValue();
            } else {
                String tradeTimesStr = tradeTimes.toString();
                String _lastNum = tradeTimesStr.substring(tradeTimesStr.length() - 2);
                if (StringUtils.isNotBlank(_lastNum)) {
                    orderLastNum = Integer.parseInt(_lastNum);
                    logger.info("【orderLastNum 策略值】{}", orderLastNum);
                }
            }
        }
        logger.info("【{} strategy】【lastNum = {}】", filterPrefix, orderLastNum);

        if (orderLastNum < qfPercentage) {

        } else if (orderLastNum < (allinPercentage + qfPercentage)) {
            String chanType = allinStrategyMap.get(paymentRequest.getTrade_type());
            logger.info("【{} strategyMap】【channelType = {}】", filterPrefix, chanType);
            if (StringUtils.isNotBlank(chanType)) {
                paymentRequest.setTrade_type(chanType);
            }
        } else if (orderLastNum < (allinPercentage + qfPercentage + cmPercentage)) {
            String chanType = cmStrategyMap.get(paymentRequest.getTrade_type());
            logger.info("【{} strategyMap】【channelType = {}】", filterPrefix, chanType);
            if (StringUtils.isNotBlank(chanType)) {
                paymentRequest.setTrade_type(chanType);
            }
        }

        logger.info("【{} strategy】 【paymentRequest = {}】", filterPrefix, JSON.toJSONString(paymentRequest));
    }

}
